// webscoket单例模式封装

/**
 *      单例模式确保一个类只有一个实例，并提供一个全局访问点来访问当前是实例
 *      private static instance: WebSocketClient;  这是一个静态私有变量，用于存储 WebSocketClient 类的唯一实例
 *      由于用 private 申明的，所以是私有的，外部无法直接访问 或者可以用 es6 简写 #static instance: WebSocketClient;
 *
 *      public static getInstance（）是一个静态公共方法，用于获取 WebSocketClient 类的唯一实例
 *      if 检查 WebscoketClient.instance是否初始化，如果没有，则通过 new 实例化一个WebscoketClient，如果有，则直接返回 WebscoketClient.instance
 *
 *      最后通过 export default WebSocketClient.instance 暴露给全局使用
 *      可通过 import WebSocketClient from /***  导入，然后 const name = WebSocketClient(参数)
 *
 *      优点：
 *            唯一实例：确保整个应用中只有一个 WebSocket 连接，避免重复创建和资源浪费。
 *            状态一致：所有代码访问的是同一个实例，状态管理更加简单和一致。
 *            集中管理：方便集中管理和维护 WebSocket 连接，减少代码复杂性。
 *            性能优化：避免不必要的资源占用，提升应用性能。
 */

interface heartBaetMsgType {
  msg: 'ping'
  time: number
}

class WebsocketClient {
  private static instance: WebsocketClient
  private socket!: WebSocket
  private heartBeat: { interval: number; reconnect: number } = {
    interval: 5 * 1000, // 心跳检测时间
    reconnect: 10 * 1000, // 断线重连时间
  }
  private webSocketState: boolean = false
  private heartBeatMsg: heartBaetMsgType = {
    // 心跳消息
    msg: 'ping',
    time: Date.now(),
  }
  private heartBeatTimeout: number = 10 * 1000 // 心跳超时时间

  constructor(public url: string) {
    this.connect()
  }

  public static getInstance(url: string): WebsocketClient {
    if (!WebsocketClient.instance) {
      WebsocketClient.instance = new WebsocketClient(url)
    }
    return WebsocketClient.instance
  }

  connect() {
    this.socket = new WebSocket(this.url)
    this.init()
  }

  init() {
    this.socket.onopen = () => {
      console.log('ws打开链接')
      this.webSocketState = true
      // 检查依赖数据 开始心跳检测
      this.heartBeat && this.heartBeat.interval ? this.startHeartBeat(this.heartBeat.interval) : ''
    }

    this.socket.onmessage = (event: MessageEvent<any>) => {
      console.log('接收消息: ', event.data)
      switch (event.data) {
        case 'ping':
          console.log('心跳消息')
          this.startHeartBeat(this.heartBeat.interval)
          break
        case 'msg':
          console.log('其他消息')
          break
      }
    }

    this.socket.onclose = () => {
      console.log('ws断开连接')
      this.webSocketState = false
      this.reconnect()
    }

    this.socket.onerror = (error: Event) => {
      console.log('ws-err: ', error)
      this.webSocketState = false
      this.reconnect()
    }
  }

  sendMessage<T>(msg: T) {
    if (this.socket.readyState === WebSocket.OPEN) {
      return this.socket.send(JSON.stringify(msg))
    }
    console.error('webSocket未连接')
    this.reconnect()
  }

  // 心跳检测
  startHeartBeat(t: number) {
    setTimeout(() => {
      this.sendMessage<heartBaetMsgType>(this.heartBeatMsg) // 发送心跳消息
      this.waitingServer() // 延迟等待服务端响应
    }, t)
  }

  // 延迟等待服务端响应
  waitingServer() {
    setTimeout(() => {
      if (this.webSocketState) {
        this.startHeartBeat(this.heartBeat.interval) // 没有断开继续心跳
        return
      }
      this.socket.close()
      this.reconnect() // 断开重连
    }, this.heartBeatTimeout)
  }

  // 重连
  reconnect() {
    setTimeout(() => {
      this.connect()
    }, this.heartBeat.reconnect)
  }
}

export default WebsocketClient.getInstance
